using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using NUnit.Framework;
using SCSFContrib.CompositeUI.WPF.Workspaces;

namespace SCSFContrib.CompositeUI.WPF.UnitTest.Workspaces
{
	[TestFixture]
	public sealed class ZoneWorkspaceTest : UnitTest
	{
		private ZoneWorkspace _zoneWorkspace;

		protected override void SetUpCore()
		{
			base.SetUpCore();
			_zoneWorkspace = new ZoneWorkspace();
		}

		[Test]
		public void Zones_ShouldBeEmptyByDefault()
		{
			Assert.AreEqual(0, _zoneWorkspace.Zones.Count);
		}

		[Test]
		public void Zones_ShouldNotIncludeDefaultZone()
		{
			ContentControl defaultZone = new ContentControl();
			_zoneWorkspace.Content = defaultZone;
			_zoneWorkspace.DefaultZone = defaultZone;
			Assert.AreEqual(0, _zoneWorkspace.Zones.Count);
		}

		[Test]
		public void Zones_ShouldIncludeAnyConfiguredZones()
		{
			ContentControl defaultZone = new ContentControl();
			ContentControl zone1 = new ContentControl();
			ContentControl zone2 = new ContentControl();
			ContentControl zone3 = new ContentControl();
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(defaultZone);
			stackPanel.Children.Add(zone1);
			stackPanel.Children.Add(zone2);
			stackPanel.Children.Add(zone3);
			_zoneWorkspace.Content = stackPanel;
			_zoneWorkspace.DefaultZone = defaultZone;
			ZoneWorkspace.SetZoneName(zone1, "zone1");
			ZoneWorkspace.SetZoneName(zone2, "zone2");
			ZoneWorkspace.SetZoneName(zone3, "zone3");

			IDictionary<string, ContentControl> zones = _zoneWorkspace.Zones;
			Assert.AreEqual(3, zones.Count);
			Assert.AreSame(zone1, zones["zone1"]);
			Assert.AreSame(zone2, zones["zone2"]);
			Assert.AreSame(zone3, zones["zone3"]);
		}

		[Test]
		public void DefaultZone_ShouldBeNullByDefault()
		{
			Assert.IsNull(_zoneWorkspace.DefaultZone);
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), "Specified zone does not reside in a ZoneWorkspace.")]
		public void DefaultZone_ShouldThrowIfNoParentZoneWorkspaceFound()
		{
			_zoneWorkspace.DefaultZone = new ContentControl();
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), "Specified zone does not reside in a ZoneWorkspace.")]
		public void DefaultZone_ShouldThrowIfNoParentWorkspaceIsNotAZoneWorkspace()
		{
			ContentControl zone = new ContentControl();
			DeckWorkspace deckWorkspace = new DeckWorkspace();
			deckWorkspace.Content = zone;
			_zoneWorkspace.Content = deckWorkspace;
			_zoneWorkspace.DefaultZone = zone;
		}

		[Test]
		public void DefaultZone_ShouldGetAndSet()
		{
			ContentControl defaultZone = new ContentControl();
			_zoneWorkspace.Content = defaultZone;
			_zoneWorkspace.DefaultZone = defaultZone;
			Assert.AreSame(defaultZone, _zoneWorkspace.DefaultZone);
			_zoneWorkspace.DefaultZone = null;
			Assert.IsNull(_zoneWorkspace.DefaultZone);
		}

		[Test]
		public void DefaultZone_SettingToNullShouldClearZoneFromWorkspace()
		{
			ContentControl defaultZone = new ContentControl();
			_zoneWorkspace.Content = defaultZone;
			_zoneWorkspace.DefaultZone = defaultZone;
			Assert.AreSame(defaultZone, _zoneWorkspace.DefaultZone);
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(defaultZone));

			_zoneWorkspace.DefaultZone = null;
			Assert.IsNull(_zoneWorkspace.DefaultZone);
			Assert.IsNull(ZoneWorkspace.GetWorkspace(defaultZone));
		}

		[Test]
		public void DefaultZone_SettingToAnotherZoneShouldDisownOldDefaultZone()
		{
			ContentControl defaultZone1 = new ContentControl();
			ContentControl defaultZone2 = new ContentControl();
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(defaultZone1);
			stackPanel.Children.Add(defaultZone2);
			_zoneWorkspace.Content = stackPanel;
			_zoneWorkspace.DefaultZone = defaultZone1;
			Assert.AreSame(defaultZone1, _zoneWorkspace.DefaultZone);
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(defaultZone1));

			_zoneWorkspace.DefaultZone = defaultZone2;
			Assert.AreSame(defaultZone2, _zoneWorkspace.DefaultZone);
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(defaultZone2));
			Assert.IsNull(ZoneWorkspace.GetWorkspace(defaultZone1));
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void GetZoneName_ShouldThrowIfNoZone()
		{
			ZoneWorkspace.GetZoneName(null);
		}

		[Test]
		public void GetZoneName_ShouldYieldNullIfNoZoneNameSet()
		{
			Assert.IsNull(ZoneWorkspace.GetZoneName(new ContentControl()));
			ContentControl contentControl = new ContentControl();
			Assert.IsNull(ZoneWorkspace.GetZoneName(contentControl));
			Assert.IsNull(ZoneWorkspace.GetZoneName(contentControl));
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void SetZoneName_ShouldThrowIfNoZone()
		{
			ZoneWorkspace.SetZoneName(null, "name");
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), "Specified zone does not reside in a ZoneWorkspace.")]
		public void SetZoneName_ShouldThrowIfNoParentZoneWorkspaceFound()
		{
			ContentControl zone = new ContentControl();
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(zone);
			ZoneWorkspace.SetZoneName(zone, "zone");
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), "Specified zone does not reside in a ZoneWorkspace.")]
		public void SetZoneName_ShouldThrowIfNoParentWorkspaceIsNotAZoneWorkspace()
		{
			ContentControl zone = new ContentControl();
			DeckWorkspace deckWorkspace = new DeckWorkspace();
			deckWorkspace.Content = zone;
			_zoneWorkspace.Content = deckWorkspace;
			ZoneWorkspace.SetZoneName(zone, "zone");
		}

		[Test]
		public void SetZoneName_ShouldAssignGivenNameToZone()
		{
			ContentControl zone = new ContentControl();
			_zoneWorkspace.Content = zone;
			Assert.IsNull(ZoneWorkspace.GetZoneName(zone));
			ZoneWorkspace.SetZoneName(zone, "name");
			Assert.AreEqual("name", ZoneWorkspace.GetZoneName(zone));
			ZoneWorkspace.SetZoneName(zone, "another name");
			Assert.AreEqual("another name", ZoneWorkspace.GetZoneName(zone));
		}

		[Test]
		public void SetZoneName_SettingToNullShouldClearZoneFromZoneWorkspace()
		{
			ContentControl zone = new ContentControl();
			_zoneWorkspace.Content = zone;
			ZoneWorkspace.SetZoneName(zone, "name");

			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(zone));
			IDictionary<string, ContentControl> zones = GetPrivateMemberValue<IDictionary<string, ContentControl>>(_zoneWorkspace, "_zones");
			Assert.IsTrue(zones.ContainsKey("name"));

			ZoneWorkspace.SetZoneName(zone, null);
			Assert.IsFalse(zones.ContainsKey("name"));
			Assert.IsNull(ZoneWorkspace.GetWorkspace(zone));
		}

		[Test]
		public void SetZoneName_ShouldAllowZoneToBeRenamed()
		{
			ContentControl zone = new ContentControl();
			_zoneWorkspace.Content = zone;
			ZoneWorkspace.SetZoneName(zone, "name");

			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(zone));
			IDictionary<string, ContentControl> zones = GetPrivateMemberValue<IDictionary<string, ContentControl>>(_zoneWorkspace, "_zones");
			Assert.IsTrue(zones.ContainsKey("name"));

			ZoneWorkspace.SetZoneName(zone, "another name");
			Assert.IsFalse(zones.ContainsKey("name"));
			Assert.AreEqual(1, zones.Count);
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(zone));
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void GetWorkspace_ShouldThrowIfZoneIsNull()
		{
			ZoneWorkspace.GetWorkspace(null);
		}

		[Test]
		public void GetWorkspace_ShouldYieldNullByDefault()
		{
			Assert.IsNull(ZoneWorkspace.GetWorkspace(new ContentControl()));
		}

		[Test]
		public void GetWorkspace_ShouldYieldWorkspaceForAnyConfiguredZones()
		{
			ContentControl defaultZone = new ContentControl();
			ContentControl zone1 = new ContentControl();
			ContentControl zone2 = new ContentControl();
			ContentControl zone3 = new ContentControl();
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(defaultZone);
			stackPanel.Children.Add(zone1);
			stackPanel.Children.Add(zone2);
			stackPanel.Children.Add(zone3);
			_zoneWorkspace.Content = stackPanel;
			_zoneWorkspace.DefaultZone = defaultZone;
			ZoneWorkspace.SetZoneName(zone1, "zone1");
			ZoneWorkspace.SetZoneName(zone2, "zone2");
			ZoneWorkspace.SetZoneName(zone3, "zone3");

			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(zone1));
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(zone2));
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(zone3));
			Assert.AreSame(_zoneWorkspace, ZoneWorkspace.GetWorkspace(defaultZone));
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "'Workspace' property was registered as read-only and cannot be modified without an authorization key.")]
		public void Workspace_ShouldBeReadOnly()
		{
			_zoneWorkspace.SetValue(ZoneWorkspace.WorkspaceProperty, _zoneWorkspace);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnApplySmartPartInfo_ShouldThrowIfSmartPartIsNull()
		{
			_zoneWorkspace.OnApplySmartPartInfo(null, new ZoneSmartPartInfo());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnApplySmartPartInfo_ShouldThrowIfSmartPartInfoIsNull()
		{
			_zoneWorkspace.OnApplySmartPartInfo(new ContentControl(), null);
		}

		[Test]
		public void OnApplySmartPartInfo_ShouldMoveSmartPartIntoSpecifiedZone()
		{
			StackPanel stackPanel = new StackPanel();
			ContentControl zone1 = new ContentControl();
			ContentControl zone2 = new ContentControl();
			string smartPart = "abc";
			stackPanel.Children.Add(zone1);
			stackPanel.Children.Add(zone2);
			_zoneWorkspace.Content = stackPanel;
			ZoneWorkspace.SetZoneName(zone1, "zone1");
			ZoneWorkspace.SetZoneName(zone2, "zone2");

			_zoneWorkspace.Show(smartPart, new ZoneSmartPartInfo("zone1"));
			Assert.AreSame(smartPart, zone1.Content);
			_zoneWorkspace.ApplySmartPartInfo(smartPart, new ZoneSmartPartInfo("zone2"));
			Assert.IsNull(zone1.Content);
			Assert.AreSame(smartPart, zone2.Content);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnShow_ShouldThrowIfSmartPartIsNull()
		{
			_zoneWorkspace.OnShow(null, new ZoneSmartPartInfo());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnShow_ShouldThrowIfSmartPartInfoIsNull()
		{
			_zoneWorkspace.OnShow("abc", null);
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage="No zone name was specified and no default zone has been configured. You must either specify the zone name or assign a default zone to the ZoneWorkspace.")]
		public void OnShow_ShouldThrowIfNoDefaultZoneAndNoZoneNameGiven()
		{
			_zoneWorkspace.Show("abc");
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Zone 'someZone' was not found in this ZoneWorkspace.")]
		public void OnShow_ShouldThrowIfSpecifiedZoneIsNotFound()
		{
			ContentControl defaultZone = new ContentControl();
			_zoneWorkspace.Content = defaultZone;
			_zoneWorkspace.DefaultZone = defaultZone;
			_zoneWorkspace.Show("abc", new ZoneSmartPartInfo("someZone"));
		}

		[Test]
		public void OnShow_ShouldShowInDefaultZoneIfNoZoneNameGiven()
		{
			ContentControl defaultZone = new ContentControl();
			_zoneWorkspace.Content = defaultZone;
			_zoneWorkspace.DefaultZone = defaultZone;
			_zoneWorkspace.Show("abc");
			Assert.AreEqual("abc", defaultZone.Content);
		}

		[Test]
		public void OnShow_ShouldShowSmartPartInSpecifiedZone()
		{
			ContentControl defaultZone = new ContentControl();
			ContentControl zone1 = new ContentControl();
			ContentControl zone2 = new ContentControl();
			ContentControl zone3 = new ContentControl();
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(defaultZone);
			stackPanel.Children.Add(zone1);
			stackPanel.Children.Add(zone2);
			stackPanel.Children.Add(zone3);
			_zoneWorkspace.Content = stackPanel;
			_zoneWorkspace.DefaultZone = defaultZone;
			ZoneWorkspace.SetZoneName(zone1, "zone1");
			ZoneWorkspace.SetZoneName(zone2, "zone2");
			ZoneWorkspace.SetZoneName(zone3, "zone3");

			_zoneWorkspace.Show(smartPart1);
			Assert.AreEqual(smartPart1, defaultZone.Content);

			_zoneWorkspace.Show(smartPart1, new ZoneSmartPartInfo("zone1"));
			Assert.IsNull(defaultZone.Content);
			Assert.AreEqual(smartPart1, zone1.Content);

			_zoneWorkspace.Show(smartPart2, new ZoneSmartPartInfo("zone2"));
			Assert.AreEqual(smartPart2, zone2.Content);

			_zoneWorkspace.Show(smartPart3, new ZoneSmartPartInfo("zone3"));
			Assert.AreEqual(smartPart3, zone3.Content);

			_zoneWorkspace.Show(smartPart1, new ZoneSmartPartInfo("zone3"));
			Assert.IsNull(zone1.Content);
			Assert.AreEqual(smartPart1, zone3.Content);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnActivate_ShouldThrowIfSmartPartIsNull()
		{
			_zoneWorkspace.OnActivate(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnActivate_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_zoneWorkspace.OnActivate("abc");
		}

		[Test]
		public void OnActivate_EachZoneShouldActLikeADeck()
		{
			ContentControl defaultZone = new ContentControl();
			ContentControl zone = new ContentControl();
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(zone);
			_zoneWorkspace.Content = stackPanel;
			ZoneWorkspace.SetZoneName(zone, "zone");

			_zoneWorkspace.Show(smartPart1, new ZoneSmartPartInfo("zone"));
			Assert.AreEqual("abc", zone.Content);
			_zoneWorkspace.Show(smartPart2, new ZoneSmartPartInfo("zone"));
			Assert.AreEqual("def", zone.Content);
			_zoneWorkspace.Show(smartPart3, new ZoneSmartPartInfo("zone"));
			Assert.AreEqual("ghi", zone.Content);

			_zoneWorkspace.Hide(smartPart3);
			Assert.AreEqual("def", zone.Content);
			_zoneWorkspace.Hide(smartPart2);
			Assert.AreEqual("abc", zone.Content);
			_zoneWorkspace.Hide(smartPart1);
			Assert.AreEqual("ghi", zone.Content);
			_zoneWorkspace.Hide(smartPart1);
			Assert.AreEqual("ghi", zone.Content);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnHide_ShouldThrowIfSmartPartIsNull()
		{
			_zoneWorkspace.OnHide(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnHide_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_zoneWorkspace.OnHide("abc");
		}

		[Test]
		public void OnHide_ShouldHideSpecifiedSmartPart()
		{
			ContentControl defaultZone = new ContentControl();
			ContentControl zone = new ContentControl();
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";
			string smartPart4 = "jkl";
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(defaultZone);
			stackPanel.Children.Add(zone);
			_zoneWorkspace.Content = stackPanel;
			_zoneWorkspace.DefaultZone = defaultZone;
			ZoneWorkspace.SetZoneName(zone, "zone");

			_zoneWorkspace.Show(smartPart1);
			_zoneWorkspace.Show(smartPart2);
			_zoneWorkspace.Show(smartPart3, new ZoneSmartPartInfo("zone"));
			_zoneWorkspace.Show(smartPart4, new ZoneSmartPartInfo("zone"));

			Assert.AreEqual("def", defaultZone.Content);
			Assert.AreEqual("jkl", zone.Content);

			_zoneWorkspace.Hide(smartPart2);
			Assert.AreEqual("abc", defaultZone.Content);
			_zoneWorkspace.Hide(smartPart2);
			Assert.AreEqual("abc", defaultZone.Content);
			_zoneWorkspace.Hide(smartPart1);
			Assert.AreEqual("def", defaultZone.Content);

			_zoneWorkspace.Hide(smartPart4);
			Assert.AreEqual("ghi", zone.Content);
			_zoneWorkspace.Hide(smartPart4);
			Assert.AreEqual("ghi", zone.Content);
			_zoneWorkspace.Hide(smartPart3);
			Assert.AreEqual("jkl", zone.Content);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnClose_ShouldThrowIfSmartPartIsNull()
		{
			_zoneWorkspace.OnClose(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnClose_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_zoneWorkspace.OnClose("abc");
		}

		[Test]
		public void OnClose_ShouldShowSmartPartBehind()
		{
			ContentControl defaultZone = new ContentControl();
			ContentControl zone1 = new ContentControl();
			ContentControl zone2 = new ContentControl();
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";
			string smartPart4 = "jkl";
			string smartPart5 = "mno";
			string smartPart6 = "pqr";
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(zone1);
			stackPanel.Children.Add(zone2);
			_zoneWorkspace.Content = stackPanel;
			ZoneWorkspace.SetZoneName(zone1, "zone1");
			ZoneWorkspace.SetZoneName(zone2, "zone2");

			_zoneWorkspace.Show(smartPart1, new ZoneSmartPartInfo("zone1"));
			_zoneWorkspace.Show(smartPart4, new ZoneSmartPartInfo("zone2"));
			_zoneWorkspace.Show(smartPart2, new ZoneSmartPartInfo("zone1"));
			_zoneWorkspace.Show(smartPart5, new ZoneSmartPartInfo("zone2"));
			_zoneWorkspace.Show(smartPart3, new ZoneSmartPartInfo("zone1"));
			_zoneWorkspace.Show(smartPart6, new ZoneSmartPartInfo("zone2"));

			IDictionary<object, string> smartPartZoneNames = GetPrivateMemberValue<IDictionary<object, string>>(_zoneWorkspace, "_smartPartZoneNames");

			Assert.IsTrue(smartPartZoneNames.ContainsKey(smartPart3));
			_zoneWorkspace.Close(smartPart3);
			Assert.IsFalse(smartPartZoneNames.ContainsKey(smartPart3));
			Assert.AreEqual("def", zone1.Content);
			_zoneWorkspace.Show(smartPart3, new ZoneSmartPartInfo("zone1"));
			Assert.AreEqual("ghi", zone1.Content);
			Assert.IsTrue(smartPartZoneNames.ContainsKey(smartPart3));
			_zoneWorkspace.Close(smartPart3);
			Assert.IsFalse(smartPartZoneNames.ContainsKey(smartPart3));
			Assert.AreEqual("def", zone1.Content);
			Assert.IsTrue(smartPartZoneNames.ContainsKey(smartPart2));
			_zoneWorkspace.Close(smartPart2);
			Assert.IsFalse(smartPartZoneNames.ContainsKey(smartPart2));
			Assert.AreEqual("abc", zone1.Content);
			Assert.IsTrue(smartPartZoneNames.ContainsKey(smartPart1));
			_zoneWorkspace.Close(smartPart1);
			Assert.IsFalse(smartPartZoneNames.ContainsKey(smartPart1));
			Assert.IsNull(zone1.Content);
			_zoneWorkspace.Show(smartPart1, new ZoneSmartPartInfo("zone1"));
			Assert.AreEqual("abc", zone1.Content);
		}
	}
}